package com.chiancloud.shield.common.params;


import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.chiancloud.shield.core.exception.BadCommandArgumentsException;
import com.chiancloud.shield.core.exception.ErrorStrings;
import com.chiancloud.shield.core.exception.ShieldException;
import com.chiancloud.shield.tools.ShieldUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public abstract class CommonArgs extends ArgOps implements ShieldActions,
        Arguments {

    protected static final Logger log = LoggerFactory.getLogger(CommonArgs.class);


    @Parameter(names = ARG_HELP, help = true)
    public boolean help;
    public Map<String, String> definitionMap = new HashMap<>();
    public Map<String, String> syspropsMap = new HashMap<>();

    public final JCommander commander;
    private final String[] args;

    private AbstractActionArgs coreAction;

    public String getClusterName() {
        return coreAction.getClusterName();
    }

    public CommonArgs(String[] args) {
        this.args = args;
        commander = new JCommander(this);
    }

    public CommonArgs(Collection args) {
        List<String> argsAsStrings = ShieldUtils.collectionToStringList(args);
        this.args = argsAsStrings.toArray(new String[argsAsStrings.size()]);
        commander = new JCommander(this);
    }


    public String usage() {
        StringBuilder builder = new StringBuilder("\n");
        commander.usage(builder, "  ");
        builder.append("\nactions: ");
        return builder.toString();
    }

    public void parse() throws ShieldException {
        addActionArguments();
        try {
            commander.parse(getArgs());
        } catch (ParameterException e) {
            throw new BadCommandArgumentsException(e, "%s in %s",
                    e.toString(),
                    (getArgs() != null
                            ? (ShieldUtils.join(getArgs(),
                            " ", false))
                            : "[]"));
        }
        postProcess();
    }


    protected void addAction(String name, Object arg) {
        commander.addCommand(name, arg);
    }

    protected void addActions(Object... actions) {
        for (Object action : actions) {
            commander.addCommand(action);
        }
    }


    protected void addActionArguments() {

    }


    public void postProcess() throws ShieldException {
        applyAction();
        validate();


        for (Map.Entry<String, String> entry : syspropsMap.entrySet()) {
            System.setProperty(entry.getKey(), entry.getValue());
        }
    }


    public abstract void applyAction() throws ShieldException;


    protected void bindCoreAction(AbstractActionArgs action) {
        coreAction = action;

        splitPairs(coreAction.definitions, definitionMap);
        splitPairs(coreAction.sysprops, syspropsMap);
    }


    public AbstractActionArgs getCoreAction() {
        return coreAction;
    }


    public void validate() throws BadCommandArgumentsException {
        if (coreAction == null) {
            throw new BadCommandArgumentsException(ErrorStrings.ERROR_NO_ACTION
                    + usage());
        }
        log.debug("action={}", getAction());
        coreAction.validate();
    }


    public void applyDefinitions(Configuration conf) throws
            BadCommandArgumentsException {
        applyDefinitions(definitionMap, conf);
    }


    public void applyFileSystemURL(Configuration conf) {
        ArgOps.applyFileSystemURL(getFilesystemURL(), conf);
    }

    public boolean isDebug() {
        return coreAction.debug;
    }


    public URI getFilesystemURL() {
        return coreAction.filesystemURL;
    }

    public Path getBasePath() {
        return coreAction.basePath;
    }

    public String getManager() {
        return coreAction.manager;
    }

    public String getAction() {
        return commander.getParsedCommand();
    }

    public List<String> getActionArgs() {
        return coreAction.parameters;
    }

    public String[] getArgs() {
        return args;
    }
}
